package com.lean.reptile.Jsoup;

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName: JsoupDemo
 * @Description: Jsoup是一款基于java语言的html解析器，可以直接对网页URL，文件，字符串文本进行解析，并且可对生成的DOM结构进行增删改查操作
 * jsoup 是一款Java 的HTML解析器，可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API，可通过DOM，CSS以及类似于jQuery的操作方法来取出和操作数据。
 * 功能
 * 从URL，文件或字符串中抓取并解析HTML
 * 使用DOM遍历或CSS选择器查找和提取数据
 * 处理HTML元素，属性和文本
 * 根据安全的白名单清除用户提交的内容，以防止XSS攻击
 * 输出整洁的HTML
 * jsoup旨在处理野外发现的所有各种HTML；从原始和验证到无效的标签汤；jsoup将创建一个明智的解析树。
 * @Author: zhanghongwei
 * @Date: 2022/5/7 15:50
 */
public class JsoupDemo {

//            <dependency>
//                <groupId>org.jsoup</groupId>
//                <artifactId>jsoup</artifactId>
//                <version>1.14.3</version>
//           </dependency>-
//    jsoup封装了http请求所涉及的几乎所有api，在Jsoup.connect()方法返回的对象Connection对象中，封装了http请求的常见操作。
//    cookie
//        Connection cookie(String k, String v);
//
//        Connection cookies(Map<String, String> cookieMap);
//        支持添加kv形式的单个cookie，同时支持多个cookie的Map的添加，简单直接
//    get post execute method
//        Document get() throws IOException;
//
//        Document post() throws IOException;
//
//        Connection.Response execute() throws IOException;
//
//        Connection method(Method method);
//        封装了常见的请求方式，execute请求默认为get请求
//
//    URL
//        Connection url(URL var1);
//
//        Connection url(String var1);
//        支持多种url请求
//    Proxy
//        Connection proxy(Proxy var1);
//
//        Connection proxy(String var1, int var2);
//        支持代理请求
//
//    data requestBody
//        Connection data(String var1, String var2);
//
//        Connection data(String var1, String var2, InputStream var3);
//
//        Connection data(String var1, String var2, InputStream var3, String var4);
//
//        Connection data(Collection<Connection.KeyVal> var1);
//
//        Connection data(Map<String, String> var1);
//
//        Connection data(String... var1);
//
//        Connection requestBody(String body);
//        支持各种各样的请求data入参
//    followRedirects
//        Connection followRedirects(boolean var1);
//        当当前请求返回状态码为302需要重定向时，是否自动重定向，默认为true即默认进行重定向请求
//
//    userAgent
//        Connection userAgent(String userAgent);
//        添加请求头部的UserAgent客户端信息
//
//    ignoreContentType
//        Connection ignoreContentType(boolean ignoreContentType);
//        是否忽略请求返回的数据类型，默认为false，当请求类型不支持时会抛出异常(Unhandled content type. Must be text/, application/xml, or application/+xml)，设置为true则忽略
//
//    ignoreHttpErrors
//        Connection ignoreHttpErrors(boolean ignoreHttpErrors);
//
//    header headers
//        Connection header(String name, String value);
//
//        Connection headers(Map<String,String> headers);
//        添加请求头部
//    referrer
//        Connection referrer(String referrer);
//        设置请求头referrer来源
//
//    response request
//        Response response();
//        Request request();
//        获取当前请求的request response对象
//
//    Document
//        Document继承自Element，Document是一个特殊的Element，因为他表示整个DOM的根节点，上面加载文档返回的类型就是Document对象，Docuement中有一个很重要的对象parser，表示解析当前DOM树的Parse解析器
//
//        以下是document常用方法，更多方法可以参考Jsoup文档 https://jsoup.org/
//
//        body()，获取body元素节点
//        select()，强大的select选择器，支持通过css获取对应的节点元素信息
//        getElementById()，通过ID获取元素节点
//        attr(String)，获取当前节点对应的属性信息
//        attr(String,String)，设置当前节点属性信息
//        text()，获取当前节点内容信息
//        text(String)，设置当前节点内容信息
//        outerHtml()，获取整个html节点信息
//        children()，获取当前节点下的所有子节点
//        className()，获取当前节点class名称

//    查找元素
//        getElementById(String id)
//        getElementsByTag(String tag)
//        getElementsByClass(String className)
//        getElementsByAttribute(String key) (and related methods)
//        Element siblings: siblingElements(), firstElementSibling(), lastElementSibling(); nextElementSibling(), previousElementSibling()
//        Graph: parent(), children(), child(int index)
//    元素数据
//        attr(String key)获取属性attr(String key, String value)设置属性
//        attributes()获取所有属性
//        id(), className() and classNames()
//        text()获取文本内容text(String value) 设置文本内容
//        html()获取元素内HTMLhtml(String value)设置元素内的HTML内容
//        outerHtml()获取元素外HTML内容
//        data()获取数据内容（例如：script和style标签)
//        tag() and tagName()
//    操作HTML和文本
//        append(String html), prepend(String html)
//        appendText(String text), prependText(String text)
//        appendElement(String tagName), prependElement(String tagName)
//        html(String value)

//    通过选择器解析
//            Selector选择器概述
//    tagname: 通过标签查找元素，比如：a
//    ns|tag: 通过标签在命名空间查找元素，比如：可以用 fb|name 语法来查找 <fb:name> 元素
//    #id: 通过ID查找元素，比如：#logo
//    .class: 通过class名称查找元素，比如：.masthead
//    [attribute]: 利用属性查找元素，比如：[href]
//    [^attr]: 利用属性名前缀来查找元素，比如：可以用[^data-] 来查找带有HTML5 Dataset属性的元素
//    [attr=value]: 利用属性值来查找元素，比如：[width=500]
//    [attr^=value], [attr$=value], [attr*=value]: 利用匹配属性值开头、结尾或包含属性值来查找元素，比如：[href*=/path/]
//    [attr~=regex]: 利用属性值匹配正则表达式来查找元素，比如： img[src~=(?i)\.(png|jpe?g)]
//    *: 这个符号将匹配所有元素
//    Selector选择器组合使用
//        el#id: 元素+ID，比如： div#logo
//        el.class: 元素+class，比如： div.masthead
//        el[attr]: 元素+class，比如： a[href]
//    任意组合，比如：a[href].highlight
//        ancestor child: 查找某个元素下子元素，比如：可以用.body p 查找在"body"元素下的所有 p元素
//        parent > child: 查找某个父元素下的直接子元素，比如：可以用div.content > p 查找 p 元素，也可以用body > * 查找body标签下所有直接子元素
//        siblingA + siblingB: 查找在A元素之前第一个同级元素B，比如：div.head + div
//        siblingA ~ siblingX: 查找A元素之前的同级X元素，比如：h1 ~ p
//        el, el, el:多个选择器组合，查找匹配任一选择器的唯一元素，例如：div.masthead, div.logo
//     伪选择器selectors
//        :lt(n): 查找哪些元素的同级索引值（它的位置在DOM树中是相对于它的父节点）小于n，比如：td:lt(3) 表示小于三列的元素
//        :gt(n):查找哪些元素的同级索引值大于n，比如： div p:gt(2)表示哪些div中有包含2个以上的p元素
//        :eq(n): 查找哪些元素的同级索引值与n相等，比如：form input:eq(1)表示包含一个input标签的Form元素
//        :has(seletor): 查找匹配选择器包含元素的元素，比如：div:has(p)表示哪些div包含了p元素
//        :not(selector): 查找与选择器不匹配的元素，比如： div:not(.logo) 表示不包含 class=logo 元素的所有 div 列表
//        :contains(text): 查找包含给定文本的元素，搜索不区分大不写，比如： p:contains(jsoup)
//        :containsOwn(text): 查找直接包含给定文本的元素
//        :matches(regex): 查找哪些元素的文本匹配指定的正则表达式，比如：div:matches((?i)login)
//        :matchesOwn(regex): 查找自身包含文本匹配指定正则表达式的元素
//    注意：上述伪选择器索引是从0开始的，也就是说第一个元素索引值为0，第二个元素index为1等



    /**
     * HTML字符串
     * @throws IOException
     */
    public void  html() throws IOException {
        Document document = Jsoup.parse("<html><body><p><span>111</span></p></body></html>");
        Elements p = document.select("p span");
        System.out.println(p.text());
    }

    /**
     * 解析URL
     * @throws IOException
     */
    public void  test() throws IOException {
        Document document = Jsoup.connect("https://gitee.com/").get();
        String title = document.title();
        System.out.println(title);
    }

    /**
     * 解析文件
     * @throws IOException
     */
    public void  file() throws IOException {
        Document document = Jsoup.parse(new File("./doc/a.html"),"utf-8");
        Element title = document.getElementsByTag("title").first();
        System.out.println(title.text());
    }

    /**
     * 爬取网页数据
     */
    public List<Content> parseJD(String keyWords) throws IOException {
        String url = "https://search.jd.com/Search?keyword=" + keyWords;
        Connection connect = Jsoup.connect(url);
        connect.header("cookie","unpl=JF8EAKtnNSttXU0HBx4BTBESSQpUW1oBGEdTaWFVVVoPHFxWElcdF0N7XlVdXhRKEx9uYRRUWlNPVw4aCisSEXteXVdZDEsWC2tXVgQFDQ8VXURJQlZAFDNVCV9dSRZRZjJWBFtdT1xWSAYYRRMfDlAKDlhCR1FpMjVkXlh7VAQrCxsaEUJfVlttOEwnM19hB1BVWUpkBCsDK1BEF15WX14OQ1oKb28EXV9aTmQEKwE; __jdv=76161171|baidu-pinzhuan|t_288551095_baidupinzhuan|cpc|0f3d30c8dba7459bb52f2eb5eba8ac7d_0_47b342f233f1468baa77a06ff9b8d74b|1651913529855; __jdu=81908334; areaId=19; ipLoc-djd=19-1607-0-0; PCSYCityID=CN_440000_440300_0; shshshfpa=fdaec9fd-bda6-bd1c-9a14-0f155ac84ac4-1651913531; shshshfpb=ddtsQLfUeekdqT-MUVIMWxw; __jda=122270672.81908334.1651913529.1651913529.1651913530.1; __jdc=122270672; shshshfp=3f983f7094ec10ae39782260affd89e2; rkv=1.0; __jdb=122270672.3.81908334|1.1651913530; shshshsID=480bc4fa86ffcb0198c1ccf58d66a608_3_1651913815752; qrsc=2; 3AB9D23F7A4B3C9B=SSU3PE72LZWJTRL24LBSH53MLEC5X6UWMDNP5ZZ3QRYCN3LRPRO2MPHZCTTUM6RXD3FY42IB3JCHGBVVLZRZ5XTZQU");
        connect.header("user-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36");
        connect.referrer("https://www.jd.com/");

        //https://proxy.mimvp.com/freeopen
//        connect.proxy()

        Document document = connect.get();
        Element element = document.getElementById("J_goodsList");
        Elements elements = element.getElementsByTag("li");
        ArrayList<Content> contents = new ArrayList<>();
        for (Element el : elements) {
            String img = el.select("div > div.p-img > a > img").attr("data-lazy-img");
//            String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
            String price = el.getElementsByClass("p-price").eq(0).text();
            String title = el.getElementsByClass("p-name").eq(0).text();
            Content content = new Content();
            content.setImg("http:"+img);
            content.setTitle(title);
            content.setPrice(price);
            contents.add(content);
        }

        return contents;
    }

    public static void main(String[] args) throws IOException {
        JsoupDemo jsoupDemo=new JsoupDemo();
//        jsoupDemo.file();
//        jsoupDemo.html();
        jsoupDemo.parseJD("go").forEach(System.out::println);
    }



}
